package com.zb.orm.ext;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.SerializationUtils;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;

import com.zb.orm.meta.EntityColumnMetadata;
import com.zb.orm.meta.EntityMetadata;
import com.zb.util.ArrayUtil;
import com.zb.util.JsonUtils;
import com.zb.util.ObjectUtils;

/**
 * 实体类插入器
 * 
 * 作者: zhoubang 日期：2015年3月26日 下午1:29:20
 * 
 * @param <T>
 */
public class InsertCreator<T> implements PreparedStatementCreator {

    private LobHandler lobHandler;
    private T t;
    private EntityColumnMetadata[] metadatas;
    private String[] columnNames;
    private String sql;

    /**
     * <p>
     * Constructor for InsertCreator.
     * </p>
     *
     * @param lobHandler
     *            a {@link org.springframework.jdbc.support.lob.LobHandler}
     *            object.
     * @param t
     *            a T object.
     * @param entityMetadata
     *            a {@link cn.yicha.commons.orm.meta.EntityMetadata} object.
     */
    public InsertCreator(LobHandler lobHandler, T t, EntityMetadata<T> entityMetadata) {
        this.lobHandler = lobHandler;
        this.t = t;
        this.metadatas = ArrayUtil.toArray(entityMetadata.getPrimaryKey().isAuto() ? entityMetadata.getAllColumnMetadatas() : entityMetadata.getAllColumnMetadatasNoPK(),
                EntityColumnMetadata.class);
        this.columnNames = new String[metadatas.length];
        StringBuffer sb = new StringBuffer();
        sb.append("insert into ").append(entityMetadata.getQualifiedTableName()).append("(");
        StringBuffer sb2 = new StringBuffer();
        for (int i = 0, len = this.metadatas.length; i < len; i++) {
            EntityColumnMetadata bar = this.metadatas[i];
            sb.append(bar.getNameWithQuote()).append((i == len - 1) ? ") values(" : ",");
            sb2.append("?").append((i == len - 1) ? ")" : ",");
            this.columnNames[i] = bar.getName();
        }
        sb.append(sb2);
        this.sql = sb.toString();
    }

    /** {@inheritDoc} */
    @Override
    public PreparedStatement createPreparedStatement(Connection con)
            throws SQLException {
        final PreparedStatement ps = con.prepareStatement(this.sql, this.columnNames);
        for (int i = 0, len = this.metadatas.length; i < len; i++) {
            EntityColumnMetadata foo = this.metadatas[i];
            if (!foo.isLob()) {
                ps.setObject(i + 1, ObjectUtils.getValue(this.t, foo.getField()));
            } else {
                try {
                    this.fillLob(ps, i + 1, ObjectUtils.getValue(this.t, foo.getField()));
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new SQLException(e);
                }
            }
        }
        return ps;
    }

    private void fillLob(PreparedStatement ps, int i, Object value)
            throws SQLException, IOException {
        LobCreator lobCreator = this.lobHandler.getLobCreator();
        if (value instanceof InputStream) {
            lobCreator.setBlobAsBinaryStream(ps, i, (InputStream) value, ((InputStream) value).available());
        } else if (value instanceof Reader) {
            lobCreator.setClobAsString(ps, i, IOUtils.toString((Reader) value));
        } else if (value instanceof String) {
            lobCreator.setClobAsString(ps, i, (String) value);
        } else if (value instanceof byte[] || value instanceof Byte[]) {
            lobCreator.setBlobAsBytes(ps, i, (byte[]) value);
        } else if (value instanceof Serializable) {
            lobCreator.setBlobAsBytes(ps, i, SerializationUtils.serialize((Serializable) value));
        } else {
            lobCreator.setClobAsString(ps, i, JsonUtils.toJson(value));
        }
    }
}
